﻿/* //////////////////////////////////////////////////////////////////////////
 * FormUp Class
 * Para tratamento de formulários
 *
 * @author Guilherme Almeida
 * @version 2.0
 * 
 * 17 Jun 07 (2.0) - Versão revisada
 * 22 Mai 07 (1.3) - Upload files
 * 17 Mai 07 (1.2) - Suporte a AMFPHP
 * 29 Mar 07 (1.1)
 *
 * Site: http://www.g3iaction.com/formup
 * Blog: http://www.g3iaction.com/blog
 * Documentação: http://code.google.com/p/formup/
 *
 * 
 * /////////////////// Licensed under the MIT License /////////////////////////
 
Copyright (c) 2007 Guilherme Almeida and Rodrigo Moyle

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

http://code.google.com/p/formup/
 
 * //////////////////////////////////////////////////////////////////////////

 //// Como usar _____________________________________________________________

import FlashForm.FormUp;

var meuForm:FormUp = new FormUp();
meuForm._arrayField[0] = {mc:'campo1'};
meuForm._arrayField[1] = {mc:'campo2', fType:'tipo', required:true};

meuForm._form = this;
meuForm._serverFile = 'cadastro.php'

meuForm.onStart = function() {
   trace('Iniciado!');
};
meuForm.onSend = function() {
   trace('Enviando...');
   this._afterSend.ready = true;
};
meuForm.onError = function(arr) {
   trace(arr[0].mc);
   trace(arr[0].msg);
};
meuForm.onComplete = function() {
   trace('Sucesso!');
};
meuForm.init();
_____________________________________________________________________________
*
*/
import flash.external.*;
import flash.net.FileReference;
import FlashForm.Formats;
import FlashForm.MaskValidate;
import FlashForm.ControlButton;
//
class FlashForm.FormUp {
	public var _form:MovieClip;
	public var _arrayField:Array;
	public var _initValues:Object;
	public var _debugData:Boolean;
	public var _restart:Boolean;
	public var _serverFile:String;
	//	public var _serverUploadFile:String;
	public var _commonError:String;
	public var _invalidFormat:String;
	public var _methodSend:String;
	public var _serverError:String;
	public var _btSend:MovieClip;
	public var _btBrowser:MovieClip;
	public var _arrayFileType:Array;
	public var _callBackFile:Object;
	public var _arrayError:Array;
	public var _afterSend:Object;
	private var enterSubmit:Object;
	private var mcFile:MovieClip;
	private var objUpload:Object;
	private var frUpload:FileReference;
	public var onStart:Function;
	public var onValidate:Function;
	public var onSend:Function;
	public var onError:Function;
	public var onComplete:Function;
	/**
	 * FormUp parametros públicos //////////////////////////////////////////////////////////////////////////
	 * Parametros necessários, que poderão ser alterados (públicos) a qualquer instante:
	 *
	 * @param	_commonError		String		Erro padrão para campo obrigatório não preenchido
	 * @param	_invalidFormat		String		Erro padrão para campo preenchido com formato inválido
	 * @param	_serverError		String		Erro genérico para erro desconhecido
	 * @param	_serverFile			String		Arquivo server-side para receber os dados do formulário	 
	 * @param	_serverUploadFile	String		Arquivo server-side para fazer upload separadamente
	 * @param	_methodSend			String		Método de envio do formulário POST e GET
	 * @param	_form				MovieClip	MC que engloba os itens do formulário
	 * @param	_arrayField			Array		Array com a definição dos campos	
	 * @param	_arrayFileType		Array		Array com os tipos de dados permitidos para upload
	 * @param	_callBackFile		Object		Object para funcões de respostas para os eventos de upload	 
	 * @param	_btBrowser			MovieClip	MovieClip usado para acionar a janela de escolha de arquivo
	 * @param	_initValues			Object		Object para iniciar form preenchido
	 * @param	_afterSend			Object		Monitora o estado de ready para envio	 
	 * @param	_arrayError			Array		Conteúdo dos erros enviado por .onError(_arrayError)
	 * @param	_restart			Boollean	Se true limpa e reinicia o formulário no .onComplete()
	 * @param	_btSend				MovieClip	MC usado como botão para submeter o formulário
	 * @param	_debugData			Boolean		Se true ativa debuger
	 *
	 */
	function FormUp() {
		System.useCodepage = true;
		this._commonError = 'Preenchimento obrigatório';
		this._invalidFormat = 'Formato inválido';
		this._serverError = 'Ocorreu um erro no servidor!';
		this._methodSend = 'POST';
		this._restart = true;
		this._arrayField = new Array();
		this._arrayFileType = new Array();
		this._initValues = new Object();
		this._afterSend = new Object();
		this._afterSend.ready = false;
		this._afterSend.watch('ready', this.callSendForm, this);
		this.enterSubmit = new Object();
		Key.addListener(this.enterSubmit);
	}
	/**
	* onSend() é o evento chamado no momento em que a validação é concluída, permitindo que você faça qualquer coisa
	* antes de submeter os dados ao servidor. Se declarar o evento .onSend(), é necessário que ele permita a 
	* continuação do envio. Ex: meuForm._afterSend.ready = true
	* Caso não precise, basta não declara-lo. 
	*
	* Para inicialiazar o form já preenchido basta definir _initValues com o nome dos 
	* campos e o valor inicial no formato: {nome:'João', gosto_musical:'ro,po,sa'}
	*
	* _btSend é o MovieClip que receberá a função de submeter o formulário, se não for definido
	* apenas crie um MovieClip com a instancia 'btSend' dentro do MC que contem todos os itens do formulário.
	*
	* FormUp funções de controle (eventos) //////////////////////////////////////////////////////////////////////////
	*
	* @param	.onStart		Function		Ao iniciar o formulário
	* @param	.onValidate		Function		Ao iniciar a validação
	* @param	.onSend			Function		Ao termino da validação do form, antes de requisitar o _serverFile
	* @param	.onError		Function		Envia o _arrayError para a função declarada para: <formup>.onErro = function(){}
	* @param	.onComplete		Function		Conclusão do envio do formulário
	*
	*/
	public function init():Void {
		var arrayCheckInit:Array = new Array();
		var val = new FlashForm.Formats();
		var tab = this._arrayField.length;
		var self = this;
		//
		if (!this.onSend) {
			this.onSend = function() {
				self._afterSend.ready = true;
			};
		}
		//                                                                                      
		for (var ind in this._arrayField) {
			var objt = this._arrayField[ind];
			var mc = this._arrayField[ind].mc;
			var clip = this._form[mc];
			//
			clip.fType = objt.fType;
			clip.fCase = objt.fCase;
			clip.field.tabIndex = --tab;
			clip.field.text = '';
			clip.ind = ind;
			clip.errorFormat = this._arrayField[ind].errorFormat;
			//
			if (objt.autoTab) {
				var nextTab:TextField = this._form[this._arrayField[++ind].mc].field;
			}
			//                                                                                                                                                                                                                                    
			if (val.types[clip.fType]) {
				clip.mask = new FlashForm.MaskValidate(val.types[clip.fType].mask, clip.field, nextTab, function (clip) {
					self.noAccept(clip);
				});
			} else if (clip.fType == 'passwd') {
				clip.field.password = true;
			} else if (objt.fType == 'radio' || objt.fType == 'check') {
				new FlashForm.ControlButton(objt.mc, objt.options, this._form, objt.fType);
			} else if (objt.fType == 'hidden') {
				this._form.createEmptyMovieClip(objt.mc, this._form.getNextHighestDepth());
				this._form[objt.mc].createTextField("field", this._form[objt.mc].getNextHighestDepth(), 0, 0, 100, 20);
				this._form[objt.mc]._visible = false;
			} else if (objt.fType == 'num') {
				clip.field.restrict = "0-9";
			} else if (objt.fType == 'file') {
				this.mcFile = clip;
				this.objUpload = new Object();
				this.frUpload = new FileReference();
				this.uploadFile();
			} else if (!clip.mask) {
				clip.field.onChanged = function() {
					this.text = self.getCase(clip, this.text);
				};
			}
		}
		//
		for (var ind in this._initValues) {
			var clip = this._form[ind];
			clip.field.text = this.getCase(clip, this._initValues[ind]);
			clip.mask ? clip.mask.addFormat() : null;
		}
		//
		if (this._form.btSend) {
			this._btSend = this._form.btSend;
		} else if (!this._btSend) {
			trace('** Error ** Não existe um _btSend ou _form definido.');
		}
		this._btSend.tabIndex = this._arrayField.length+1;
		this._btSend._focusrect = false;
		this._btSend.onRelease = function() {
			self.valid();
		};
		this.enterSubmit.onKeyUp = function() {
			if (Key.getCode() == Key.ENTER) {
				var inFocu = eval(Selection.getFocus());
				inFocu._parent.fType != 'textArea' ? self.valid() : null;
			}
		};
		this.onStart();
	}
	//
	public function noAccept(clip:MovieClip):Void {
		var msg = this._arrayField[clip.ind].msg;
		msg = msg ? msg : this._invalidFormat;
		clip.errorFormat.apply(clip, [msg]);
	}
	//
	public function clearForm():Void {
		for (var ind in this._arrayField) {
			var mc = this._arrayField[ind].mc;
			var clip = this._form[mc];
			clip.field.text = '';
		}
	}
	/**
	 * getCase function
	 * 
	 * @param	clip		MovieClip	Campo
	 * @param	fText		String		Texto contido
	 * @return	fText		String		Texto convertido para fCase do campo
	 */
	public function getCase(clip:MovieClip, fText:String):String {
		if (clip.fCase == 'upper') {
			fText = fText.toUpperCase();
		} else if (clip.fCase == 'lower') {
			fText = fText.toLowerCase();
		}
		return fText;
	}
	/**
	* A função valid verifica se os campos obrigatórios não estão vazios e se estão preenchidos corretamente.
	* Faz a validação de e-mail sem regexp.
	*/
	public function valid():Void {
		this.onValidate();
		this._arrayError = new Array();
		//
		for (var ind:Number = 0; ind<this._arrayField.length; ind++) {
			var clip = this._form[this._arrayField[ind].mc];
			clip.fType = this._arrayField[ind].fType;
			clip.required = this._arrayField[ind].required;
			clip.msg = this._arrayField[ind].msg;
			clip.keyed = clip.field.text;
			//
			if (clip.required && clip.keyed == '') {
				this._arrayError.push({mc:clip, msg:clip.msg ? clip.msg : this._commonError});
			}
			if (clip.fType == 'email' && clip.keyed != '') {
				if (clip.keyed.indexOf('@', 0) == -1 || clip.keyed.indexOf('.', 0) == -1) {
					this._arrayError.push({mc:clip, msg:clip.msg ? clip.msg : this._invalidFormat});
				}
			}
			if (clip.mask && clip.keyed != '') {
				if (!clip.mask.isValid()) {
					this._arrayError.push({mc:clip, msg:clip.msg ? clip.msg : this._invalidFormat});
				}
			}
		}
		//
		if (this._arrayError.length>0) {
			this.onError.apply(this, [this._arrayError]);
		} else {
			this.onSend.apply(this);
		}
	}
	//
	public function callSendForm(prop, oldVal, newVal, self) {
		if (newVal) {
			if (self.objUpload) {
				var urlencode:String = '?';
				for (var ind in self._arrayField) {
					if (self._arrayField[ind].fType != 'file') {
						var clip = self._form[self._arrayField[ind].mc];
						var pName = self._arrayField[ind].postName;
						var pClip = clip._name;
						var pValue = clip.field.text;
						//
						if (pName) {
							urlencode += pName+'='+escape(pValue)+'&';
						} else {
							urlencode += pClip+'='+escape(pValue)+'&';
						}
					}
				}
				self.frUpload.upload(self._serverFile+urlencode);
			} else {
				self.sendForm();
			}
		}
	}
	//
	/**
	* uploadFile é quem controla os eventos de upload, não funciona com AMFPHP, para usar a funcionalidade 
	* de campo do fType 'file' é necessário um _serverFile definido.
	*/
	public function uploadFile() {
		var self = this;
		//
		this.objUpload.onSelect = function(fFile:FileReference):Void  {
			self._callBackFile.fileSelect.apply(this, [fFile]);
			self.mcFile.field.text = fFile.name;
		};
		this.objUpload.onComplete = function(fFile:FileReference):Void  {
			self._callBackFile.fileComplete.apply(this, [fFile]);
			self.onComplete.apply(self);
			self._restart ? self.clearForm() : null;
		};
		this.objUpload.onHTTPError = this.objUpload.onIOError=function () {
			self._callBackFile.fileHTTPError.apply(this);
		};
		this.objUpload.onProgress = function(fFile:FileReference, bytesLoaded:Number, bytesTotal:Number):Void  {
			var percent:Number = Math.floor((bytesLoaded/bytesTotal)*100);
			self._callBackFile.fileProgress.apply(this, [fFile, bytesLoaded, bytesTotal, percent]);
		};
		this.objUpload.onCancel = function(fFile:FileReference):Void  {
			self._callBackFile.fileCancel.apply(this, [fFile]);
		};
		this.objUpload.onOpen = function(fFile:FileReference):Void  {
			self._callBackFile.fileOpen.apply(this, [fFile]);
		};
		this.objUpload.onIOError = function(fFile:FileReference):Void  {
			self._callBackFile.fileIOError.apply(this, [fFile]);
		};
		this.objUpload.onSecurityError = function(fFile:FileReference, errorString:String):Void  {
			self._callBackFile.fileSecurityError.apply(this, [fFile, errorString]);
		};
		//
		this.frUpload.addListener(this.objUpload);
		//
		if (this.mcFile.btBrowser) {
			this._btBrowser = this.mcFile.btBrowser;
		} else if (!this._btBrowser) {
			trace('** Error ** _btBrowser não está definido.');
		}
		this._btBrowser.onRelease = function() {
			self.frUpload.browse(self._arrayFileType);
			self._callBackFile.fileDialogOpen();
		};
	}
	/**
	* sendForm() é quem faz a requisição no servidor do arquivo definido no _serverFile.
	* Essa função inicia dois eventos, .onError(_arrayError) e onComplete().
	*
	* postName é um nó (não obrigatório) do _arrayField, serve para que o arquivo 
	* server-side receba os dados em variáves de POST/GET diferentes dos nomes dos campos no Flash, caso não 
	* especificado ficarão iguais, normalmente, bom para usar frameWorks como CakePHP [http://www.cakephp.org/].
	*
	* _debugData, chama um alert do javacript no browser printa no console do firebug [https://addons.mozilla.org/en-US/firefox/addon/1843] 
	* e da um trace() com todo conteúdo do onData, para habilitar basta definir meuForm._debugData = true	
	*
	* .onError(_arrayError) é um evento chamado recebendo um array como parâmetro, com os nós {mc, msg}
	* com o campo e a mensagem correspondente.
	*
	* .onComplete() é um evento solicitado na conclusão do server-side ao printar a variável finish=ok.
	* finish é uma variável de retorno obrigatória, que o server-side deve devolver de acordo com o caso
	* msg também é usada no caso de uma mensagem específica devido a algum tratamento externo.
	*/
	//
	public function sendForm():Void {
		if (!this._serverFile) {
			this.onComplete.apply(this);
			this._restart ? this.clearForm() : null;
			return;
		}
		this._arrayError = new Array();
		var servSide = new LoadVars();
		var self = this;
		//
		for (var ind in this._arrayField) {
			var clip = this._form[this._arrayField[ind].mc];
			if (this._arrayField[ind].postName) {
				servSide[this._arrayField[ind].postName] = clip.field.text;
			} else {
				servSide[clip._name] = clip.field.text;
			}
		}
		//
		if (this._debugData) {
			servSide.onData = function(src:String) {
				ExternalInterface.call('alert', src);
				ExternalInterface.call('console.log', src);
				trace(src);
			};
		}
		//                                                                                  
		servSide.onLoad = function(sucess:Boolean):Void  {
			if (sucess) {
				if (this.finish == 'ok') {
					self.onComplete.apply(self);
					self._restart ? self.clearForm() : null;
				} else if (this.finish == 'error') {
					self._arrayError.push({mc:'error', msg:this.msg ? this.msg : self._serverError});
					self.onError.apply(self, [self._arrayError]);
				} else {
					var txtMsg = this.msg ? this.msg : self._form[this.finish].msg;
					self._arrayError.push({mc:this.finish, msg:txtMsg ? txtMsg : self._serverError+' ['+this.finish+']'});
					self.onError(self._arrayError);
				}
			} else {
				self._arrayError.push({mc:'error', msg:self._serverError});
				self.onError(self._arrayError);
			}
		};
		servSide.sendAndLoad(this._serverFile, servSide, this._methodSend);
	}
}
